#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <grp.h>
#include <dirent.h>
#include <string.h>
#include <sys/wait.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
typedef struct parameter
{
	char* sourcefile;
	char* targetfile;
}parameter;
void printf_type(mode_t perm)
{
	char* str = "";
	if (S_ISREG(perm))  str = "regular";
	else if (S_ISDIR(perm))  str = "directory";
	else if (S_ISCHR(perm))  str = "character special";
	else if (S_ISBLK(perm))  str = "block special";
	else if (S_ISFIFO(perm)) str = "fifo";
	else if (S_ISLNK(perm))  str = "symbolic link";
	else if (S_ISSOCK(perm)) str = "socket";
	else str = "unknown mode";
	printf("printf_type:");
	printf("%s\n", str);
}
#define STR_SIZE sizeof("rwxrwxrwx")
void printf_perm(mode_t perm)
{
	char str[STR_SIZE];
	snprintf(str, STR_SIZE, "%c%c%c%c%c%c%c%c%c",
		(perm & S_IRUSR) ? 'r' : '-', (perm & S_IWUSR) ? 'w' : '-',
		(perm & S_IXUSR) ? 'x' : '-',
		(perm & S_IRGRP) ? 'r' : '-', (perm & S_IWGRP) ? 'w' : '-',
		(perm & S_IXGRP) ? 'x' : '-',
		(perm & S_IROTH) ? 'r' : '-', (perm & S_IWOTH) ? 'w' : '-',
		(perm & S_IXOTH) ? 'x' : '-');
	printf("%s\t", str);
}
void printf_link(nlink_t nlink) {
	printf("%ld\t", nlink);
}
void printf_usrname(uid_t uid) {
	struct passwd* user;
	user = getpwuid(uid);
	printf("%s\t", user->pw_name);
}
void printf_grname(gid_t gid) {
	struct group* group;
	group = getgrgid(gid);
	printf("%s\t", group->gr_name);
}
void print_size(off_t size) {
	printf("%ld\t", size);
}
void printf_time(time_t time) {
	char* mytime = NULL;
	mytime = ctime(&time);
	int n = strlen(mytime);
	mytime[n - 1] = '\t';
	printf("%s", mytime);
}
void printf_filename(struct dirent* dir) {
	printf("%s\n", dir->d_name);
}
void print(struct stat currentstat, struct dirent* currentdp) {
	printf_perm(currentstat.st_mode);
	printf_link(currentstat.st_nlink);
	printf_usrname(currentstat.st_uid);
	printf_grname(currentstat.st_gid);
	print_size(currentstat.st_size);
	printf_time(currentstat.st_mtime);
	printf_filename(currentdp);
}
void mycp(struct dirent* currentdp, char* buffer);
void recurse(void* args) {
	char* buffer = (char*)args;
	DIR* currentdir;
	struct dirent* currentdp;
	struct stat currentstat;
	//打开当前目录，并判断是否成功
	if ((currentdir = opendir(buffer)) == NULL)
	{
		printf("open directory fail\n");
	}
	//读取当前目录下的目录项，并判断是否成功
	while ((currentdp = readdir(currentdir)) != NULL)
	{
		// 实现当前目录的所有非隐藏文件的复制
		if (currentdp->d_name[0] != '.') {
			if (stat(buffer, &currentstat) != 0)
			{
				printf("get stat srror\n");
				continue;
			}
			print(currentstat, currentdp);
			mycp(currentdp, buffer);
		}
	}
	closedir(currentdir);
}

void copyfile(void* arg) {
	struct parameter p = *(parameter*)arg;
	char* buf = NULL;
	int fd_src = -1;
	int fd_dest = -1;
	int buf_size = 4096;
	int cnt = 0;
	struct flock lock;
	if ((fd_src = open(p.sourcefile, O_RDONLY)) == -1) {
		perror("open src. file");
		return;
	}
	if ((fd_dest = open(p.targetfile, O_WRONLY | O_CREAT | O_EXCL, 0664)) == -1) {
		perror("open dest. file");
		close(fd_src);
		return;
	}

	buf = sbrk(buf_size);
	if ((void*)-1 == buf) {
		perror("malloc");
		close(fd_src);
		close(fd_dest);
		return;
	}

	lock.l_type = F_RDLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_pid = -1;
	if (fcntl(fd_src, F_SETLK, &lock) == -1) {
		fprintf(stderr, "%s has been locked by other process\n", p.sourcefile);
		close(fd_src);
		close(fd_dest);
		return;
	}

	errno = 0;
	while ((cnt = read(fd_src, buf, buf_size)) > 0) {
		if (write(fd_dest, buf, cnt) < cnt) {
			perror("write");
			close(fd_src);
			close(fd_dest);
			return;
		}
	}
	if (0 != errno) {
		perror("read");
		return;
	}
	close(fd_src);
	fd_src = -1;
	close(fd_dest);
	fd_dest = -1;
	brk(buf);
}
void mycp(struct dirent* currentdp, char* buffer) {
	if (currentdp->d_type != 4) {
		pthread_t tid;
		printf("create childthread\n");
		char source[100];
		strcpy(source, buffer);
		strcat(source, "/");
		strcat(source, currentdp->d_name);
		char target[100];
		strcpy(target, "/home/xiao/XiaoYingYing/");
		strcat(target, currentdp->d_name);
		struct parameter par = {
			source,
			target
		};
		pthread_create(&tid, NULL, (void*)copyfile, &par);
		pthread_detach(tid);
		sleep(1);
	}
	else {
		char source[100];
		strcpy(source, buffer);
		strcat(source, "/");
		strcat(source, currentdp->d_name);
		char* mybuffer = source;
		recurse(mybuffer);
	}
}

int main(int argc, char* argv[]) {
	if (argc > 2) {
		printf("error");
		return 0;
	}
	char* buffer = NULL;
	buffer = (char*)(malloc(100 * sizeof(char)));
	DIR* currentdir;
	struct dirent* currentdp;
	struct stat currentstat;
	if (argc == 2) {
		buffer = argv[1];
	}
	else {
		//getcwd()会将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数maxlen为buffer的空间大小
		if (getcwd(buffer, 100 * sizeof(char)) != NULL)
		{
			printf("%s\n", buffer);
		}
	}
	pthread_t lsid;
	pthread_create(&lsid, NULL, (void*)recurse, buffer);
	pthread_join(lsid, NULL);
	return 0;
}

